/*
 * BADataBuffer.java
 * classes : com.qim.basdk.cmd.BADataBuffer
 * @author xuwei
 * V 1.0.0
 * Create at 2015年8月19日 下午3:58:16
 */
package com.langsheng.lsintell.cmd.data;

import com.langsheng.lsintell.utils.LSConst;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;

/**
 * com.qim.basdk.cmd.LSDataBuffer
 * <p>
 * 数据缓存区,指令二进制数据存储
 *
 * @author xuwei
 *         <p>
 *         created at 2015年8月19日 下午3:58:16
 */
public class LSDataBuffer {
    public final static String TAG = "LSDataBuffer";

    /**
     * 缓存数据
     */
    private byte[] buffer = new byte[0];

    /**
     * 位置指针,当buffer长度为0时,设为-1;
     */
    private int readPos = 0;
    private int writePos = 0;

    /**
     * 清空缓存区
     */
    public void clear() {
        buffer = new byte[512];
        readPos = 0;
        writePos = 0;
    }

    /**
     * 获取缓存长度
     *
     * @return
     */
    public int getLength() {
        if (readPos >= writePos) {
            return 0;
        }

        return writePos - readPos;
    }

    /**
     * 检查缓存区指令
     *
     * @return
     */
    public boolean checkCommand() {
        // 若缓存区数据小于16字节,说明包头不完整.
        if (buffer.length == 0 || getLength() < LSConst.CMD_HEAD_LENGTH) {
            return false;
        }

        int length = peekInt();
        if (length > 0 && length <= getLength()) {
            return true;
        }
        return false;
    }

    /**
     * 获取包头前四个字节,以便检查数据包完整性
     *
     * @return
     */
    private int peekInt() {
        if (buffer.length < 4) {
            return -1;
        }

        byte[] tmp = new byte[4];
        peek(tmp, 4);
        return byteArrayToInt(tmp);
    }

    /**
     * 读取第一个完整数据包的数据
     *
     * @return
     */
    public LSDataBuffer readFirstCmd() {
        if (!checkCommand()) {
            return null;
        }

        int size = peekInt();
        if (size <= 0) {
            return null;
        }
        return read(size);
    }

    /**
     * 读取缓存指定长度数据
     *
     * @param buffer
     * @param size
     * @return 返回读取到的数据的长度
     */
    private int peek(byte[] buffer, int size) {
        if (size <= 0)
            size = getLength();

        if (buffer != null && getLength() >= size) {
            System.arraycopy(this.buffer, readPos, buffer, 0, size);
        } else {
            return 0;
        }
        return size;
    }

    /**
     * 读取指定数据块, 读取数据后,将缓存里的已读数据清除
     *
     * @param buffer
     * @param size
     */
    public void read(byte[] buffer, int size) {
        int newSize = peek(buffer, size);
        offsetBuffer(newSize);
    }

    /**
     * 将缓存数据偏移指定长度
     *
     * @param size
     */
    private void offsetBuffer(int size) {
        int newPos = readPos + size;
        if (newPos >= writePos) {
            readPos = 0;
            writePos = 0;
        } else {
            readPos = newPos;
        }
    }

    /**
     * 读取指定数据块, 读取数据后,将缓存里的已读数据清除
     *
     * @param size
     */
    public LSDataBuffer read(int size) {
        if (size < 0)
            size = getLength();

        if (size <= 0)
            return new LSDataBuffer();

        byte[] tempData = new byte[size];

        read(tempData, size);

        LSDataBuffer buffer = new LSDataBuffer();

        buffer.writeByteArray(tempData);
        return buffer;
    }

    /**
     * 读取一个int数据
     *
     * @return
     */
    public int readInt() {
        byte[] tmp = new byte[LSConst.LENGTH_INT];
        read(tmp, LSConst.LENGTH_INT);
        return byteArrayToInt(tmp);
    }

    /**
     * 读取一个short数据
     *
     * @return
     */
    public short readShort() {
        byte[] tmp = new byte[LSConst.LENGTH_SHORT];
        read(tmp, LSConst.LENGTH_SHORT);
        return byteArrayToShort(tmp);
    }

    /**
     * 读取一个byte数据
     *
     * @return
     */
    public byte readByte() {
        byte[] tmp = new byte[LSConst.LENGTH_BYTE];
        read(tmp, LSConst.LENGTH_BYTE);
        return byteArrayToByte(tmp);
    }

    /**
     * 读取数据为字符串形式返回
     *
     * @return 返回utf-8编码的字符串
     */
    public String getDataString() {
        String strData = "";
        if (buffer != null) {
            strData = new String(buffer, Charset.forName(LSConst.CHARSET_UTF8));
        }
        return strData;
    }

    /**
     * 获取缓存数据
     *
     * @return
     */
    public byte[] getBytes() {
        int length = getLength();
        if (length > 0) {
            byte[] ret = new byte[length];
            System.arraycopy(buffer, readPos, ret, 0, length);
            return ret;
        } else {
            return new byte[0];
        }
    }

    /**
     * 获取未读的缓存数据
     *
     * @return
     */
    public LSDataBuffer getData() {
        byte[] data = getBytes();
        LSDataBuffer ret = new LSDataBuffer();
        ret.buffer = data;
        ret.readPos = 0;
        ret.writePos = data.length;
        return ret;
    }

    /**
     * 获取数据流
     *
     * @return
     */
    public InputStream readBufferByStream() {

        return new ByteArrayInputStream(getBytes());
    }

    /**
     * 获取字节数组
     *
     * @return
     */
    public byte[] readBufferToByteArray() {
        return getBytes();
    }

    /**
     * 分配指定大小的缓存区内存
     *
     * @param newSize
     */
    private void allocateBuffer(int newSize) {

        if (buffer.length == 0 || newSize > buffer.length - readPos) {
            byte[] tempData = new byte[newSize];
            int oldLength = getLength();
            if (getLength() > 0) {
                System.arraycopy(buffer, readPos, tempData, 0, oldLength);
            }
            buffer = tempData;
            readPos = 0;
            writePos = oldLength;
        }
    }

    /**
     * 往缓存区写入数据
     *
     * @param buf
     * @param off  写入偏移量
     * @param size
     */
    public void write(LSDataBuffer buf, int off, int size) {
        if (size == -1)
            size = buf.getLength() - off;

        if (size > 0)
            writeByteArray(buf.buffer, off, size);
    }

    /**
     * 往缓存区写入数据
     *
     * @param buffer 数据
     * @param size   写入长度
     */
    public void write(LSDataBuffer buffer, int size) {
        write(buffer, 0, size);
    }

    /**
     * 写入缓存区数据
     *
     * @param buf
     */
    public void writeByteArray(byte[] buf) {
        int size = getLength();

        // 如果缓存区不够存放数据,则分配追加新的内存
        allocateBuffer(size + buf.length);

        System.arraycopy(buf, 0, this.buffer, writePos, buf.length);
        writePos += buf.length;
    }

    /**
     * 把Buffer off处后的len个元素追加到当前缓冲区的尾部
     *
     * @param buf
     * @param off
     * @param len
     */
    public void writeByteArray(byte[] buf, int off, int len) {
        if (len == 0) {
            return;
        }

        int size = getLength();

        allocateBuffer(len + size);

        System.arraycopy(buf, off, this.buffer, writePos, len);
        writePos += len;
    }

    /**
     * 写入字符串
     *
     * @param strData
     */
    public void writeUTF8(String strData) {
        byte[] utf8 = null;
        try {
            utf8 = strData.getBytes(LSConst.CHARSET_UTF8);
        } catch (UnsupportedEncodingException e) {
            utf8 = strData.getBytes();
            e.printStackTrace();
        }
        writeByteArray(utf8);
    }

    /**
     * 写入缓存数据
     *
     * @param dataBuffer
     */
    public void write(LSDataBuffer dataBuffer) {
        writeByteArray(dataBuffer.buffer, 0, dataBuffer.getLength());
    }

    /**
     * 写入整型数据
     *
     * @param value
     */
    public void write(int value) {
        allocateBuffer(4 + getLength());

        buffer[writePos++] = (byte) (value >> 24);
        buffer[writePos++] = (byte) (value >> 16);
        buffer[writePos++] = (byte) (value >> 8);
        buffer[writePos++] = (byte) (value >> 0);
    }

    /**
     * 写入短整型数据
     *
     * @param value
     */
    public void write(short value) {
        allocateBuffer(2 + getLength());

        buffer[writePos++] = (byte) (value >> 8);
        buffer[writePos++] = (byte) (value >> 0);
    }

    /**
     * 写入byte数据
     *
     * @param value
     */
    public void write(byte value) {
        allocateBuffer(1 + getLength());

        buffer[writePos++] = value;
    }

    /**
     * byte数组转换成int
     *
     * @param data
     * @return
     */
    private static int byteArrayToInt(byte[] data) {
        if (data == null || data.length != 4) {
            return 0;
        }

        return ((data[3] & 0xFF) | ((data[2] & 0xFF) << 8) | ((data[1] & 0xFF) << 16) | ((data[0] & 0xFF) << 24));
    }

    /**
     * byte数组转换成short
     *
     * @param data
     * @return
     */
    private static short byteArrayToShort(byte[] data) {
        if (data == null || data.length != 2) {
            return 0;
        }
        return (short) ((data[1] & 0xFF) | ((data[0] & 0xFF) << 8));
    }

    /**
     * byte数组转换成byte
     *
     * @param data
     * @return
     */
    private static byte byteArrayToByte(byte[] data) {
        if (data == null || data.length != 1) {
            return 0;
        }
        return (byte) (data[0] & 0xFF);
    }

    /**
     * 缓存中查找第一个指定字符位置
     *
     * @param c
     * @return
     */
    public int findNextChar(char c) {
        for (int i = 0; i < getLength(); i++) {
            if (buffer[readPos + i] == c) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 跳过size指定大小的数据(直接读取掉)
     *
     * @param size
     */
    public void skip(int size) {
        if (size > this.buffer.length) {
            size = this.buffer.length;
        }

        byte[] tmp = new byte[size];
        read(tmp, size);
    }

    @Override
    public String toString() {

        return new String(buffer, Charset.forName("UTF-8"));
    }

}
